import Dep, { popTarget, pushTarget } from "./dep";

let id = 0;

//1.当我们创建渲染watcher的时候 我们会把当前渲染的watcher放到Dep.target上
//2.调用_render()会取值 会走到get上

//每一个属性都有一个dep(属性就是被观察者)，watcher就是观察者(属性变化了会通知观察者来更新)->观察者模式

//把渲染方法封装到Watcher中
class Wacther { //不同的组件有不同的watcher 目前只有一个 渲染根实例
    constructor(vm, exprOrFn, options,cb) {
        this.id = id++;
        this.renderWatcher = options;//是一个渲染wathcer

        if(typeof exprOrFn === 'string'){
            this.getter =function(){
                return vm[exprOrFn];
            }
        }else{
            this.getter = exprOrFn;//getter意味着调用这个函数可以发生取值操作
        }

        this.deps = [];//wathcer记录dep 后续我们实现计算属性 和一些清理工作
        this.depsId = new Set();

        this.lazy = options.lazy;
        this.dirty = this.lazy; //缓存值
        this.lazy ? undefined : this.get();
        this.vm = vm;

        this.cb=cb;
        this.value = this.user = options.user;//表标识是否是用户自己的watcher
    }
    addDep(dep) {
        //一个组件对应着多个属性 重复的属性也不用记录
        let id = dep.id;
        if (!this.depsId.has(id)) {
            this.deps.push(dep);
            this.depsId.add(id);
            dep.addSub(this);//watcher已经记住了dep而且去重了，此时的dep也记住了watcher
        }
    }
    evaluate(){
        this.value = this.get();//获取到用户函数的返回值，并且还要标识为脏
        this.dirty = false;
    }
    get() {
        // Dep.target = this;//静态属性就是只有一份
        // this.getter(); //会去vm上取值 vm._update(vm._render());
        // Dep.target = null;//渲染完再清空

        pushTarget(this);
        let value = this.getter.call(this.vm); 
        popTarget();
        return value;
    }
    depend(){
        let i = this.deps.length
        while(i--){
            //dep.depend()
            this.deps[i].depend(); //让计算属性wathcer也收集渲染wathcer
        }
    }
    update() {
        if(this.lazy){
            //如果是计算属性 依赖的值变化了 就标识计算属性是脏值
            this.dirty = true;
        }else{
            queueWatcher(this);//把当前的watcher暂存起来
            // this.get();//重新渲染 立即更新
        }
    }
    run() {
        let oldValue = this.value;
        let newValue = this.get();
        if(this.user){
            this.cb.call(this.vm,newValue,oldValue);
        }
    }
}

let queue = [];
let has = {}; // 源码用对象去重
let pending = false;//防抖

function flushSchedulerQueue() {
    let flushQueue = queue.slice(0)
    queue = [];
    has = {};
    pending = false;
    flushQueue.forEach(q => q.run());
    //在刷新的过程中 可能还有新的watcher 重新放到queue中
}

function queueWatcher(watcher) {
    const id = watcher.id;
    if (!has[id]) {
        queue.push(watcher);
        has[id] = true;
        //不管我们的update执行多少次 但是最终只执行一轮刷新操作
        if (!pending) {
            nextTick(flushSchedulerQueue, 0)
            pending = true;
        }
    }
}


//nextTick 没有直接使用某个api 而是采用优雅降级的方式
//内部先采用的是promise(ie不兼容) MutationObserver(h5的api) 
//可以考虑ie专享的 setImmediate setTimeout

let timerFunc;
if (Promise) {
    timerFunc = () => {
        Promise.resolve().then(flushCallback);
    }
} else if (MutationObserver) {
    let observer = new MutationObserver(flushCallback);//这里传入的回调是异步执行的
    let textNode = document.createTextNode(1);
    observer.observe(textNode, {
        characterData: true
    });
    timerFunc = () => {
        textNode.textContent = 2;
    }
} else if (setImmediate) {
    timerFunc = () => {
        setImmediate(flushCallback);
    }
} else {
    timerFunc = () => {
        setTimeout(flushCallback);
    }
}

let callbacks = [];
let waiting = false;
function flushCallback() {
    waiting = true;
    let cbs = callbacks.slice(0);
    callbacks = [];
    cbs.forEach(cb => cb());
}


export function nextTick(cb) {
    callbacks.push(cb);
    if (!waiting) {
        timerFunc();
        waiting = true;
        // setTimeout(()=>{
        //     flushCallback();
        // },0)
    }
}

//需要给每个属性增加一个dep，目的就是收集watcher
//一个视图中 有多少个属性(n个属性就会对应一个视图) n个dep对应一个watcher
//1个属性 对应着多个视图(组件) 1个dep对应多个watcher
//多对多的关系 一个属性例如vuex name多个组件用到 所以一个属性对应多个watcher 一个组件一个watcher会有多个属性

export default Wacther;